const WebSocket = require('ws');

// 状态机状态定义
const STATES = {
  // 连接状态
  DISCONNECTED: 'disconnected',    // 未连接状态：WebSocket连接已断开或尚未建立
  CONNECTING: 'connecting',        // 连接中状态：正在尝试建立WebSocket连接
  CONNECTED: 'connected',          // 已连接状态：WebSocket连接已建立，但尚未进入工作状态
  RECONNECTING: 'reconnecting',    // 重连中状态：连接断开后正在尝试重新连接
  
  // 音频处理状态
  IDLE: 'idle',                    // 空闲状态：已连接且ASR服务器可以接受新的音频或文件处理请求
  AUDIO_STREAMING: 'audio_streaming',     // 音频流传输状态：正在向ASR服务器发送音频数据流
  PROCESSING_AUDIO: 'processing_audio',   // 音频处理状态：ASR服务器正在处理已接收的音频数据
  
  // 文件处理状态
  FILE_UPLOADING: 'file_uploading',       // 文件上传状态：正在向ASR服务器上传音频文件
  PROCESSING_FILE: 'processing_file',     // 文件处理状态：ASR服务器正在处理已上传的音频文件
  
  // 错误状态
  ERROR: 'error'                   // 错误状态：发生了错误，需要处理或重置才能继续工作
};

// 状态转换规则
const STATE_TRANSITIONS = {
  [STATES.DISCONNECTED]: [STATES.CONNECTING, STATES.ERROR],
  [STATES.CONNECTING]: [STATES.CONNECTED, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.CONNECTED]: [STATES.IDLE, STATES.DISCONNECTED, STATES.RECONNECTING, STATES.ERROR],
  [STATES.RECONNECTING]: [STATES.CONNECTING, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.IDLE]: [STATES.AUDIO_STREAMING, STATES.FILE_UPLOADING, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.AUDIO_STREAMING]: [STATES.PROCESSING_AUDIO, STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.PROCESSING_AUDIO]: [STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.FILE_UPLOADING]: [STATES.PROCESSING_FILE, STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.PROCESSING_FILE]: [STATES.IDLE, STATES.DISCONNECTED, STATES.ERROR],
  [STATES.ERROR]: [STATES.DISCONNECTED, STATES.CONNECTING]
};

class ASRClient {
  constructor(url = 'ws://localhost:8888') {
    this.url = url;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
    this.reconnectDelay = 1000;
    this.m_latestASRResult = "";
    this.m_asrResultRecvCallback = null;
    
    // 状态机初始化
    this.currentState = STATES.DISCONNECTED;
    this.stateHistory = [];
    this.stateChangeCallbacks = new Map();
    
    this.connect();
  }

  getLatestASRResult() {
    return this.m_latestASRResult;
  }

  setASRResultRecvCallback(callback) {
    this.m_asrResultRecvCallback = callback;
  }

  removeASRResultRecvCallback() {
    this.m_asrResultRecvCallback = null;
  }

  // 状态机方法
  setState(newState, data = null) {
    if (!this.isValidStateTransition(this.currentState, newState)) {
      console.warn(`Invalid state transition from ${this.currentState} to ${newState}`);
      return false;
    }
    
    const previousState = this.currentState;
    this.currentState = newState;
    
    // 记录状态历史
    this.stateHistory.push({
      from: previousState,
      to: newState,
      timestamp: new Date(),
      data: data
    });
    
    // 限制历史记录长度
    if (this.stateHistory.length > 100) {
      this.stateHistory.shift();
    }
    
    console.log(`State changed: ${previousState} -> ${newState}`);
    
    // 触发状态变化回调
    this.notifyStateChange(previousState, newState, data);
    
    return true;
  }
  
  isValidStateTransition(fromState, toState) {
    // 处理边界条件
    if (!fromState || !toState || typeof fromState !== 'string' || typeof toState !== 'string') {
      return false;
    }
    
    const allowedTransitions = STATE_TRANSITIONS[fromState];
    return allowedTransitions && allowedTransitions.includes(toState);
  }
  
  getCurrentState() {
    return this.currentState;
  }
  
  getStateHistory() {
    // 返回深拷贝以避免外部修改
    return this.stateHistory.map(entry => ({
      from: entry.from,
      to: entry.to,
      timestamp: new Date(entry.timestamp),
      data: entry.data ? JSON.parse(JSON.stringify(entry.data)) : entry.data
    }));
  }
  
  onStateChange(callback) {
    const id = Date.now() + Math.random();
    this.stateChangeCallbacks.set(id, callback);
    return id; // 返回ID用于取消监听
  }
  
  offStateChange(id) {
    return this.stateChangeCallbacks.delete(id);
  }
  
  notifyStateChange(fromState, toState, data) {
    this.stateChangeCallbacks.forEach(callback => {
      try {
        callback(fromState, toState, data);
      } catch (err) {
        console.error('State change callback error:', err);
      }
    });
  }
  
  canPerformAction(action) {
    const actionStateMap = {
      'connect': [STATES.DISCONNECTED],
      'selectMode': [STATES.IDLE],
      'sendAudio': [STATES.IDLE, STATES.AUDIO_STREAMING],
      'endAudio': [STATES.AUDIO_STREAMING, STATES.PROCESSING_AUDIO],
      'uploadFile': [STATES.IDLE]
    };
    
    const allowedStates = actionStateMap[action];
    return allowedStates && allowedStates.includes(this.currentState);
  }

  connect() {
    if (!this.canPerformAction('connect')) {
      console.warn(`Cannot connect in current state: ${this.currentState}`);
      return;
    }
    
    this.setState(STATES.CONNECTING);
    this.ws = new WebSocket(this.url);
    this.setupEventListeners();
  }

  setupEventListeners() {
    this.ws.on('open', () => {
      console.log('Connected to ASR server');
      this.reconnectAttempts = 0;
      this.setState(STATES.CONNECTED);
      this.setState(STATES.IDLE);
    });

    this.ws.on('message', (data) => {
      try {
        const message = JSON.parse(data);
        this.handleServerMessage(message);
      } catch (err) {
        console.error('Failed to parse message:', err);
        this.setState(STATES.ERROR, { error: err.message });
      }
    });

    this.ws.on('close', () => {
      console.log('Disconnected from ASR server');
      this.setState(STATES.DISCONNECTED);
      this.attemptReconnect();
    });

    this.ws.on('error', (err) => {
      console.error('WebSocket error:', err);
      this.setState(STATES.ERROR, { error: err.message });
      this.ws.close();
    });
  }

  attemptReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
      this.setState(STATES.RECONNECTING);
      setTimeout(() => this.connect(), this.reconnectDelay);
    } else {
      console.error('Max reconnection attempts reached');
      this.setState(STATES.ERROR, { error: 'Max reconnection attempts reached' });
    }
  }

  handleServerMessage(message) {
    switch (message.type) {
      case 'mode_selected':
        console.log(`Mode selected: ${message.mode}`);
        if (this.currentState !== STATES.IDLE) {
          this.setState(STATES.IDLE);
        }
        break;
        
      case 'recognition_result':
        console.log(`Recognition result: ${message.text}`);
        this.m_latestASRResult = message.text;
        
        if(this.m_asrResultRecvCallback) {
          this.m_asrResultRecvCallback();
        }

        if (this.currentState === STATES.PROCESSING_AUDIO) {
          this.setState(STATES.IDLE, { result: message.text });
        }
        break;
        
      case 'file_recognition_result':
        console.log(`File recognition result: ${message.text}`);
        console.log("State just after ASR result:"+this.currentState);
        this.m_latestASRResult = message.text;

        if(this.m_asrResultRecvCallback) {
          this.m_asrResultRecvCallback();
        }

        if (this.currentState === STATES.PROCESSING_FILE) {
          this.setState(STATES.IDLE, { result: message.text });
        }
        break;
        
      case 'audio_chunk_received':
        // 服务器确认收到音频块
        if (this.currentState === STATES.AUDIO_STREAMING) {
          // 保持当前状态，继续流式传输
          console.log("${message.text}");
        }
        break;
        
      case 'audio_processing_started':
        if (this.currentState === STATES.AUDIO_STREAMING) {
          this.setState(STATES.PROCESSING_AUDIO);
        }
        break;
        
      case 'file_upload_complete':
        if (this.currentState === STATES.FILE_UPLOADING) {
          this.setState(STATES.PROCESSING_FILE);
        }
        break;
        
      case 'error':
        console.error(`Server error: ${message.message}`);
        this.setState(STATES.ERROR, { error: message.message });
        break;
        
      default:
        console.log('Unknown message type:', message.type);
    }
  }

  selectMode(mode) {
    if (!this.canPerformAction('selectMode')) {
      console.warn(`Cannot select mode in current state: ${this.currentState}`);
      return false;
    }
    
    console.log("Sending select mode to ASR server. New mode:" + mode);
    try {
      this.ws.send(JSON.stringify({
        type: 'select_mode',
        mode: mode
      }));
      return true;
    } catch (err) {
      console.error('Failed to send mode selection:', err);
      this.setState(STATES.ERROR, { error: err.message });
      return false;
    }
  }

  startAudioStream() {
    if (!this.canPerformAction('sendAudio')) {
      console.warn(`Cannot start audio stream in current state: ${this.currentState}`);
      return false;
    }
    
    this.setState(STATES.AUDIO_STREAMING);
    return true;
  }

  setAudioParams(sampleRate, bitDepth, numChannels) {
    if (!this.canPerformAction('sendAudio')) {
      console.warn(`Cannot set audio params in current state: ${this.currentState}`);
      return false;
    }
    console.log("setAudioParams:"+sampleRate+"...");
    try {
      this.ws.send(JSON.stringify({
        type: 'set_audio_params',
        sample_rate: sampleRate,
        bit_depth: bitDepth,
        channel_cnt: numChannels
      }));
      return true;
    } catch (err) {
      console.error('Failed to set audio params:', err);
      this.setState(STATES.ERROR, { error: err.message });
      return false;
    }
  }

  sendAudioChunk(audioData) {
    if (!this.canPerformAction('sendAudio')) {
      console.warn(`Cannot send audio in current state: ${this.currentState}`);
      return false;
    }
    
    try {
      // 如果还没开始流式传输，先设置状态
      if (this.currentState === STATES.IDLE) {
        this.setState(STATES.AUDIO_STREAMING);
      }
      
      this.ws.send(JSON.stringify({
        type: 'audio_chunk',
        audio: audioData.toString('base64')
      }));
      return true;
    } catch (err) {
      console.error('Failed to send audio chunk:', err);
      this.setState(STATES.ERROR, { error: err.message });
      return false;
    }
  }

  endAudio() {
    if (!this.canPerformAction('endAudio')) {
      console.warn(`Cannot end audio in current state: ${this.currentState}`);
      return false;
    }
    
    try {
      this.ws.send(JSON.stringify({
        type: 'end_audio'
      }));
      
      // 转换到处理状态
      if (this.currentState === STATES.AUDIO_STREAMING) {
        this.setState(STATES.PROCESSING_AUDIO);
      }
      return true;
    } catch (err) {
      console.error('Failed to end audio:', err);
      this.setState(STATES.ERROR, { error: err.message });
      return false;
    }
  }

  uploadFile(fileData, filename) {
    if (!this.canPerformAction('uploadFile')) {
      console.warn(`Cannot upload file in current state: ${this.currentState}`);
      return false;
    }
    
    try {
      this.setState(STATES.FILE_UPLOADING);
      this.ws.send(JSON.stringify({
        type: 'file_upload',
        file_data: fileData.toString('base64'),
        filename: filename
      }));
      return true;
    } catch (err) {
      console.error('Failed to upload file:', err);
      this.setState(STATES.ERROR, { error: err.message });
      return false;
    }
  }

  // 新增的状态查询方法
  isConnected() {
    return [STATES.CONNECTED, STATES.IDLE, STATES.AUDIO_STREAMING, 
            STATES.PROCESSING_AUDIO, STATES.FILE_UPLOADING, 
            STATES.PROCESSING_FILE].includes(this.currentState);
  }

  isIdle() {
    return this.currentState === STATES.IDLE;
  }

  isProcessing() {
    return [STATES.PROCESSING_AUDIO, STATES.PROCESSING_FILE, 
            STATES.AUDIO_STREAMING, STATES.FILE_UPLOADING].includes(this.currentState);
  }

  hasError() {
    return this.currentState === STATES.ERROR;
  }

  // 重置错误状态
  resetError() {
    if (this.currentState === STATES.ERROR) {
      this.setState(STATES.DISCONNECTED);
      return true;
    }
    return false;
  }
}

module.exports = ASRClient;